/**
 * 
 */
package mk.jug.academy.testing.bdd;

import mk.jug.academy.exception.BusinessException;

import org.junit.runner.RunWith;
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;

import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.googlecode.instinct.expect.ExpectThat;
import com.googlecode.instinct.expect.ExpectThatImpl;
import com.googlecode.instinct.integrate.junit4.InstinctRunner;
import com.googlecode.instinct.marker.annotate.BeforeSpecification;

/**
 * Base class for the integration behavior based on instinct, driven by the
 * BDD (Behavior Driven Development) paradigm.
 * 
 * The sub-classing test will inherit the location of the Spring app context
 * to be as 'applicationContext.xml', but the location can
 * be overridden by overriding this{@link #getConfigLocations()} method.
 * 
 * @author <a href="mailto:ice.penov@devmindset.com">Ice Penov</a>
 */
@RunWith(InstinctRunner.class) @SuppressWarnings("deprecation")
//NOTE: Deprecated class (for Spring 3) used since the pointer replacement is not working with InstinctRunner.
//TODO: Patch and commit the replacement from Spring to make it work with InstinctRunner. 
public abstract class IntegrationBehavior extends AbstractTransactionalDataSourceSpringContextTests {
    
	final protected static ExpectThat expect = new ExpectThatImpl();
	
	LocalServiceTestHelper helper = new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());
	
	@BeforeSpecification
	public void before() {
		beforeHook();
		helper.setUp();
	}
	
	protected void beforeHook() {};
			
	
  /**
   * {@inheritDoc}
   */
  @Override
  protected String[] getConfigLocations() {
    return new String[] {"classpath*:/applicationContext.xml"};
  }
  
  /**
   * Embedded class that validates that the requested
   * exception is thrown in the execute() method.
   *
   * @author Ice.Penov
   */
  protected abstract class ExceptionExpectation {
	  
	  /**
	   * Checks for the occurrence of the supplied exception, with the supplied message.
	   */
	  public void checkFor(Class<? extends Exception> exception, String withMessage) {
		  boolean thrown = false;
		  try {
			  execute();
		  } catch(Exception e) {
			  if (e.getClass().equals(exception)) {
				  if (e.getMessage().equals(withMessage)) {					  
					  thrown = true;
				  }
			  }
		  }
		  if (!thrown) {
			  //fail the test.
			  throw new RequiredExceptionNotThrown("The required exception [" + exception + "], with the message [" + withMessage + "] was not thrown in the body of the executed method.");			  
		  }
	  }
	  
	  /**
	   * Checks for the occurrence of the supplied exception. 
	   */
	  public void checkFor(Class<Exception> exception) {
		  boolean thrown = false;
		  try {
			  execute();
		  } catch(Exception e) {
			  if (e.getClass().equals(exception)) {
				  thrown = true;
			  }
		  }
		  if (!thrown) {
			  //fail the test.
			  throw new RequiredExceptionNotThrown("The required exception [" + exception + "] was not thrown in the body of the executed method.");			  
		  }
	  }
	  
	  /**
	   * Checks for the occurrence of a {@link BusinessException}, with the given errorCode.
	   */
	  public void checkForErrorCode(Class<BusinessException> exception, String errorCode) {
		  boolean thrown = false;
		  try {
			  execute();
		  } catch(BusinessException e) {
			  if (e.getClass().equals(exception)) {
				  if (e.getErrorCode().equals(errorCode)) {					  
					  thrown = true;
				  }
			  }
		  }
		  if (!thrown) {
			  //fail the test.
			  throw new RequiredExceptionNotThrown("The required exception [" + exception + "] was not thrown in the body of the executed method.");			  
		  }
	  }	  
	  
	  /**
	   * Here the sub-classing entity (usually through anonymous class), supplies the method calls
	   * that should result in an exception.
	   */
	  public abstract void execute();
	  
  }
  
  private class RequiredExceptionNotThrown extends RuntimeException {
	private static final long serialVersionUID = 2542059807013211737L;

	public RequiredExceptionNotThrown(String message) {
		  super(message);
	}
  }
  
}